package com.jy.common.tool;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoField;
import java.time.temporal.ChronoUnit;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

/**
 * 范占国
 * 工具类：日期时间相关
 */
public class ToolForDateTime {
    private static final TimeZone timeZone = TimeZone.getTimeZone("GMT");

    /**
     * 常用的时间格式
     */
    public static final String format_DateTime = "yyyy-MM-dd HH:mm:ss";
    public static final String format_DateTimeLong = "yyyy-MM-dd HH:mm:ss SSS";
    public static final String format_Date = "yyyy-MM-dd";
    public static final String format_Time = "HH:mm:ss";

    /*
     * 【特别注意！！！】
     * 不能在内部使用独立单引号，因为单引号是特殊字符。
     * 若要使用单引号则在格式字符串中使用两个连续的单引号。
     */

    /**
     * 时间格式成分：时区
     * 标识               例子                   含义
     * z                 PDT                    太平洋夏令时
     * zz                PDT                    太平洋夏令时
     * zzz               PDT                    太平洋夏令时
     * zzzz              Pacific Daylight Time  太平洋夏令时
     * Z                 +0800                  东八区时区（北京时间也是东八区）
     * ZZ                +0800                  东八区时区（北京时间也是东八区）
     * ZZZ               +0800                  东八区时区（北京时间也是东八区）
     * ZZZZ              GMT+08:00              东八区时区（北京时间也是东八区）
     * v                 PT                     太平洋时间
     * vvvv              Pacific Time           太平洋时间
     */
    private static final String TIME_ZONE_ZZZ = "ZZZ";
    private static final String TIME_ZONE_ZZZZ = "ZZZZ";

    /**
     * 时间格式成分：纪元
     * Era-替换为当前日期的Era字符串。缩写形式为一至三个字母，长形式为四个字母，窄形式为五个字母。
     * G        AD
     * GG       AD
     * GGG      AD
     * 4        Anno
     * 4        Nomini
     * 5        A
     */
    private static final String ERA_ONE_BIT = "G";
    private static final String ERA_TWO_BIT = "GG";
    private static final String ERA_THREE_BIT = "GGG";
    private static final String ERA_FOUR_BIT = "GGGG";
    private static final String ERA_FIVE_BIT = "GGGGG";

    /**
     * 时间格式成分：季度
     * 独立季度-使用数字标识季度
     * 标识      例子                 含义
     * q        1                   第一季度
     * qq       01                  第一季度
     * qqq      Q1                  第一季度
     * qqqq     first quarter       第一季度
     */
    private static final String QUARTER_ONE_BIT = "q";
    private static final String QUARTER_TWO_BIT = "qq";
    private static final String QUARTER_THREE_BIT = "qqq";
    private static final String QUARTER_FOUR_BIT = "qqqq";

    /**
     * 时间格式成分：年中周、月中周
     * 标识               例子                  含义
     * w                 7                     该周是该年的第7个周
     * ww                07                    该周是该年的第7个周
     * W                 3                     该周是该月的第3周
     */
    private static final String WEEK_OF_YEAR_ONE_BIT = "w";
    private static final String WEEK_OF_YEAR_TWO_BIT = "ww";
    private static final String WEEK_OF_MONTH = "W";

    /**
     * 时间格式成分：星期（周中日）
     * 标识               例子                  含义
     * E                 Tues                  星期二
     * EE                Tues                  星期二
     * EEE               Tues                  星期二
     * EEEE              Tuesday               星期二
     * EEEEE             T                     星期二
     * e                 2                     星期二
     * ee                02                    星期二
     * eee               Tues                  星期二
     * eeee              Tuesday               星期二
     * eeeee             T                     星期二
     */
    private static final String DAY_OF_WEEK_ONE_BIT = "E";
    private static final String DAY_OF_WEEK_TWO_BIT = "EE";
    private static final String DAY_OF_WEEK_THREE_BIT = "EEE";
    private static final String DAY_OF_WEEK_FOUR_BIT = "EEEE";
    private static final String DAY_OF_WEEK_FIVE_BIT = "EEEEE";
    private static final String LOCAL_DAY_OF_WEEK_ONE_BIT = "e";
    private static final String LOCAL_DAY_OF_WEEK_TWO_BIT = "ee";
    private static final String LOCAL_DAY_OF_WEEK_THREE_BIT = "eee";
    private static final String LOCAL_DAY_OF_WEEK_FOUR_BIT = "eeee";
    private static final String LOCAL_DAY_OF_WEEK_FIVE_BIT = "eeeee";

    /**
     * 时间格式成分：日中毫秒（当前时间点相对于当天00:00:00 00000000这个时刻相差的毫秒数）
     * 标识               值域
     * A                 [0,69540000]
     * AA                [00,69540000]
     * AAA               [000,69540000]
     * AAAA              [0000,69540000]
     * ……
     * AAAAAAAA          [00000000,69540000]
     */
    private static final String MILLISECONDS_IN_DAY = "AAAAAAAA";

    /**
     * 时间格式成分：年
     * y：年份是多少就显示多少，少不补0
     * yy：年份2位以下用0补齐2位，超过2位取最低两位
     * yyy：年份3位以下用0补齐3位，超过3位正常显示
     * yyyy：年份4位以下用0补齐4位，超过4位正常显示
     * yyyyy：年份5位以下用0补齐5位，超过5位正常显示
     * <p>
     * Year	    y	    yy	    yyy	    yyyy	    yyyyy
     * AD 1	    1	    01	    001	    0001	    00001
     * AD 12	12	    12	    012	    0012	    00012
     * AD 123	123	    23	    123	    0123	    00123
     * AD 1234	1234	34	    1234	1234	    01234
     * AD 12345	12345	45	    12345	12345	    12345
     */
    private static final String YEAR_ONE_BIT = "y";
    private static final String YEAR_TWO_BIT = "yy";
    private static final String YEAR_THREE_BIT = "yyy";
    private static final String YEAR_FOUR_BIT = "yyyy";
    private static final String YEAR_FIVE_BIT = "yyyyy";

    /**
     * 时间格式成分：月份
     * 标识               例子                  含义
     * M                 9                     九月
     * MM                09                    九月
     * MMM               Sept                  九月
     * MMMM              September             九月
     * MMMMM             S                     九月
     */
    private static final String MONTH_ONE_BIT = "M";
    private static final String MONTH_TWO_BIT = "MM";
    private static final String MONTH_THREE_BIT = "MMM";
    private static final String MONTH_FOUR_BIT = "MMMM";
    private static final String MONTH_FIVE_BIT = "MMMMM";

    /**
     * 时间格式成分：日期
     * 标识               例子                  含义
     * d                 1                    每月的一号
     * dd                01                   每月的一号
     * D                 365                  每年的第365天
     * DD                365                  每年的第365天
     * DDD               365                  每年的第365天
     * F                 2                    指定日期是当前月份第二个星期：例如：2021-01-22当天是星期五，会返回4，表示22号是01月的第4个星期五
     * g：说明：改进型的朱利安日，具体含义不明
     * gg：说明：改进型的朱利安日，具体含义不明
     * ggg：说明：改进型的朱利安日，具体含义不明
     * gggg：说明：改进型的朱利安日，具体含义不明
     * ggggg：说明：改进型的朱利安日，具体含义不明
     * gggggg：说明：改进型的朱利安日，具体含义不明
     * gg…g…gg：说明：改进型的朱利安日，具体含义不明
     */
    private static final String DAY_OF_MONTH_ONE_BIT = "d";
    private static final String DAY_OF_MONTH_TWO_BIT = "dd";
    private static final String DAY_OF_YEAR_ONE_BIT = "D";
    private static final String DAY_OF_YEAR_TWO_BIT = "DD";
    private static final String DAY_OF_YEAR_THREE_BIT = "DDD";

    /**
     * 时间格式成分：上午和下午
     * 标识               例子                  含义
     * a                 AM                    上午
     * a                 PM                    下午
     */
    private static final String PERIOD = "a";

    /**
     * 时间格式成分：小时
     * 标识               值域
     * h                 [1,12]
     * hh                [01,12]
     * H                 [0,23]
     * HH                [00,23]
     * K                 [0,11]
     * KK                [00,11]
     * k                 [1,24]
     * kk                [01,24]
     */
    private static final String HOUR_1_TO_12 = "h";
    private static final String HOUR_01_TO_12 = "hh";
    private static final String HOUR_0_TO_23 = "H";
    private static final String HOUR_00_TO_23 = "HH";
    private static final String HOUR_0_TO_11 = "K";
    private static final String HOUR_00_TO_11 = "KK";
    private static final String HOUR_1_TO_24 = "k";
    private static final String HOUR_01_TO_24 = "kk";

    /**
     * 时间格式成分：分钟
     * 标识               值域
     * m                 [0,59]
     * mm                [00,59]
     */
    private static final String MINUTE_ONE_BIT = "m";
    private static final String MINUTE_TWO_BIT = "mm";

    /**
     * 时间格式成分：秒
     * 标识               值域
     * s                 [0,59]
     * ss                [00,59]
     * S                 [0,59]
     * SS                [00,59]
     * SSS               [00.0,59.9]
     * SSSS              [00.00,59.99]
     * SSSSS             [00.000,59.999]
     * SSSSSS            [00.0000,59.9999]
     * SS…S…SS           [00.00……00,59.99……99]
     */
    private static final String SECOND_0_TO_59 = "s";
    private static final String SECOND_00_TO_59 = "ss";

    /**
     * 时间格式成分：毫秒（当前时间不足一秒的那一部分）
     * 标识               值域
     * SSS               [0,999]
     */
    private static final String MILLISECONDS = "SSS";

    /**
     * 获取当前指定时区的Calendar对象
     *
     * @param hour 相当于格林威治时间的小时数（用以表示时区）
     * @return 指定时区的Calendar对象
     */
    public static Calendar getNowCalendar(Integer hour) {
        Calendar calendar = Calendar.getInstance(timeZone);
        calendar.add(Calendar.HOUR, hour);
        return calendar;
    }

    /**
     * 根据时间格式、时间字符串来解析时间，得到对应的Calendar
     *
     * @param format     时间字符串的解析格式
     * @param dateString 时间字符串
     * @return 指定时间字符串在指定格式下对应的Calender对象
     * @throws ParseException 当dateString的格式与指定的format格式不一致时，抛出此异常
     */
    public static Calendar getCalendarByDateString(String format, String dateString, Integer timeZoneHour) throws ParseException {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
        Date date = simpleDateFormat.parse(dateString);
        Calendar calendar = Calendar.getInstance(timeZone);
        calendar.setTime(date);
        calendar.add(Calendar.HOUR, timeZoneHour);
        return calendar;
    }

    /**
     * 根据Date获取一个Calendar对象（根据timeZoneHour获取对应的Calendar对象）
     */
    public static Calendar getCalendarByConvertTimeZone(Calendar originalCalendar, Integer originalTimeZoneHour, Integer objectiveTimeZoneHour) {
        Calendar calendar = Calendar.getInstance(timeZone);
        calendar.setTime(originalCalendar.getTime());
        calendar.add(Calendar.HOUR, objectiveTimeZoneHour - originalTimeZoneHour);
        return calendar;
    }

    /**
     * 获取指定Calendar的年份
     *
     * @param calendar 已指定了时区的Calendar对象
     * @return 如：2020等
     */
    public static Integer getYearByCalendar(Calendar calendar) {
        return calendar.get(Calendar.YEAR);
    }

    /**
     * 获取指定时区当前的年份
     *
     * @param hour 相对于格林威治时间偏移的小时数（用以表示时区）
     * @return 如：2020等
     */
    public static Integer getNowYear(Integer hour) {
        Calendar calendar = Calendar.getInstance(timeZone);
        calendar.add(Calendar.HOUR, hour);
        return getYearByCalendar(calendar);
    }

    /**
     * 获取指定Calendar的月份
     *
     * @param calendar 已指定了时区的Calendar对象
     * @return 闭区间[1, 12]
     */
    public static Integer getMonthByCalendar(Calendar calendar) {
        return calendar.get(Calendar.MONTH) + 1;
    }

    /**
     * 获取指定时区当前的月份
     *
     * @param hour 相对于格林威治时间偏移的小时数（用以表示时区）
     * @return 闭区间[1, 12]
     */
    public static Integer getNowMonth(Integer hour) {
        Calendar calendar = Calendar.getInstance(timeZone);
        calendar.add(Calendar.HOUR, hour);
        return getMonthByCalendar(calendar);
    }

    /**
     * 获取指定Calendar的日期
     *
     * @param calendar 已指定了时区的Calendar对象
     * @return 闭区间[1~31]
     */
    public static Integer getDayByCalendar(Calendar calendar) {
        return calendar.get(Calendar.DAY_OF_MONTH);
    }

    /**
     * 获取指定时区当前日期
     *
     * @param hour 相对于格林威治时间偏移的小时数（用以表示时区）
     * @return 闭区间[1~31]
     */
    public static Integer getNowDay(Integer hour) {
        Calendar calendar = Calendar.getInstance(timeZone);
        calendar.add(Calendar.HOUR, hour);
        return getDayByCalendar(calendar);
    }

    /**
     * 获取指定Calendar的星期
     *
     * @param calendar 已指定了时区的Calendar对象
     * @return 闭区间[1~7]
     */
    public static Integer getWeekByCalendar(Calendar calendar) {
        calendar.setFirstDayOfWeek(Calendar.MONDAY);
        int week = calendar.get(Calendar.DAY_OF_WEEK) - 1;
        return week == 0 ? 7 : week;
    }

    /**
     * 获取指定时区当前星期几
     *
     * @param hour 相对于格林威治时间偏移的小时数（用以表示时区）
     * @return 闭区间[1~7]
     */
    public static Integer getNowWeek(Integer hour) {
        Calendar calendar = Calendar.getInstance(timeZone);
        calendar.add(Calendar.HOUR, hour);
        return getWeekByCalendar(calendar);
    }

    /**
     * 获取指定Calendar的小时数
     *
     * @param calendar 已指定了时区的Calendar对象
     * @return 闭区间[0~23]
     */
    public static Integer getHourByCalendar(Calendar calendar) {
        return calendar.get(Calendar.HOUR_OF_DAY);
    }

    /**
     * 获取指定时区当前的小时数
     *
     * @param hour 相对于格林威治时间偏移的小时数（用以表示时区）
     * @return 闭区间[0, 23]
     */
    public static Integer getNowHour(Integer hour) {
        Calendar calendar = Calendar.getInstance(timeZone);
        calendar.add(Calendar.HOUR, hour);
        return getHourByCalendar(calendar);
    }

    /**
     * 获取指定Calendar的分钟数
     *
     * @param calendar 已指定了时区的Calendar对象
     * @return 闭区间[0~59]
     */
    public static Integer getMinuteByCalendar(Calendar calendar) {
        return calendar.get(Calendar.MINUTE);
    }

    /**
     * 获取指定时区当前的分钟数
     *
     * @param hour 相对于格林威治时间的小时数（用以表示时区）
     * @return 闭区间[0, 59]
     */
    public static Integer getNowMinute(Integer hour) {
        Calendar calendar = Calendar.getInstance(timeZone);
        calendar.add(Calendar.HOUR, hour);
        return getMinuteByCalendar(calendar);
    }

    /**
     * 获取指定Calendar的秒数
     *
     * @param calendar 已指定了时区的Calendar对象
     * @return 闭区间[0~59]
     */
    public static Integer getSecondByCalendar(Calendar calendar) {
        return calendar.get(Calendar.SECOND);
    }

    /**
     * 获取指定时区当前的秒数
     *
     * @param hour 相当于格林威治时间的小时数（用以表示时区）
     * @return 闭区间[0, 59]
     */
    public static Integer getNowSecond(Integer hour) {
        Calendar calendar = Calendar.getInstance(timeZone);
        calendar.add(Calendar.HOUR, hour);
        return getSecondByCalendar(calendar);
    }

    /**
     * 获取指定Calendar的毫秒数
     *
     * @param calendar 已指定了时区的Calendar对象
     * @return 闭区间[0~999]
     */
    public static Integer getMillisecondByCalendar(Calendar calendar) {
        return calendar.get(Calendar.MILLISECOND);
    }

    /**
     * 获取指定时区当前的毫秒数
     *
     * @param hour 相当于格林威治时间的小时数（用以表示时区）
     * @return 闭区间[0, 999]
     */
    public static Integer getNowMillisecond(Integer hour) {
        Calendar calendar = Calendar.getInstance(timeZone);
        calendar.add(Calendar.HOUR, hour);
        return getMillisecondByCalendar(calendar);
    }

    /**
     * 获取指定Calendar当月的最大号数
     *
     * @param calendar 已指定了时区的Calendar对象
     * @return 闭区间[28~31]
     */
    public static Integer getMaxDayForCalendar(Calendar calendar) {
        calendar.add(Calendar.MONTH, 1);
        calendar.set(Calendar.DAY_OF_MONTH, 1);
        calendar.add(Calendar.DAY_OF_YEAR,-1);
        return getDayByCalendar(calendar);
    }

    /**
     * 获取指定时区当前月的最大号数
     *
     * @param hour 相当于格林威治时间的小时数（用以表示时区）
     * @return 闭区间[28~31]
     */
    public static Integer getNowMaxDay(Integer hour) {
        Calendar calendar = Calendar.getInstance(timeZone);
        calendar.add(Calendar.HOUR, hour);
        return getMaxDayForCalendar(calendar);
    }

    /**
     * 将Calendar转成String
     */
    public static String getStringByCalendarAndFormat(Calendar calendar, String format) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
        simpleDateFormat.setCalendar(calendar);
        return simpleDateFormat.format(calendar.getTime());
    }

    /**
     * 获取本月第一个时刻的日历对象
     * @param hour 所在时区相对于零时区偏移的小时数
     * @return 该时区所在月份1号 00:00:00 000时刻的日历对象
     */
    public static Calendar getNowMonthFirstTime(Integer hour){
        Calendar calendar = ToolForDateTime.getNowCalendar(hour);
        calendar.set(Calendar.DAY_OF_MONTH,1);
        calendar.set(Calendar.HOUR_OF_DAY,0);
        calendar.set(Calendar.MINUTE,0);
        calendar.set(Calendar.SECOND,0);
        calendar.set(Calendar.MILLISECOND,0);
        return calendar;
    }

    /**
     * 获取本月最后一个时刻的日历对线
     * @param hour 所在时区相对于零时区偏移的小时数
     * @return 该时区所在月份最后一天的23:59:59 999时刻的日历对象
     */
    public static Calendar getNowMonthLastTime(Integer hour){
        Calendar calendar = ToolForDateTime.getNowCalendar(hour);
        int day = ToolForDateTime.getMaxDayForCalendar(calendar);
        calendar.set(Calendar.DAY_OF_MONTH,day);
        calendar.set(Calendar.HOUR_OF_DAY,23);
        calendar.set(Calendar.MINUTE,59);
        calendar.set(Calendar.SECOND,59);
        calendar.set(Calendar.MILLISECOND,999);
        return calendar;
    }

    /**
     * 获取今年2月有几天（可以用来区分平年还是闰年）
     * @param hour 所在地区时区相对于零时区偏移的小时数
     * @return 指定时区对应当年二月有几天：[28,29]
     */
    public static Integer getNowYearFebruaryLastDay(Integer hour){
        Calendar calendar = ToolForDateTime.getNowCalendar(hour);
        calendar.set(Calendar.MONTH,1);
        return ToolForDateTime.getMaxDayForCalendar(calendar);
    }

    /**
     * 将LocalDateTime转成Calendar
     */
    public static Calendar getCalendarByLocalDateTime(LocalDateTime localDateTime) {
        int year = localDateTime.getYear();
        int month = localDateTime.getMonthValue() - 1;
        int day = localDateTime.getDayOfMonth();
        int hour = localDateTime.getHour();
        int minute = localDateTime.getMinute();
        int second = localDateTime.getSecond();
        int millis = localDateTime.get(ChronoField.MILLI_OF_SECOND);
        return new Calendar.Builder() {{
            setDate(year, month, day);
            setTimeOfDay(hour, minute, second, millis);
        }}.build();
    }

    /**
     * 将Calendar转成LocalDateTime
     */
    public static LocalDateTime getLocalDateTime(Calendar calendar) {
        return LocalDateTime.of(
                ToolForDateTime.getYearByCalendar(calendar)
                , ToolForDateTime.getMonthByCalendar(calendar)
                , ToolForDateTime.getDayByCalendar(calendar)
                , ToolForDateTime.getHourByCalendar(calendar)
                , ToolForDateTime.getMinuteByCalendar(calendar)
                , ToolForDateTime.getSecondByCalendar(calendar)
                , 0
        );
    }

    /**
     * 获取今年指定月份第一个时刻的日历对象
     *
     * @param month 指定的月份，参数范围[1,12]
     * @param hour  所在时区相对于零时区偏移的小时数
     * @return 该时区指定月份1号 00:00:00 000时刻的日历对象
     */
    public static Calendar getNowYearFirstTimeByMonth(Integer month, Integer hour) {
        Calendar calendar = ToolForDateTime.getNowCalendar(hour);
        calendar.set(Calendar.MONTH, month - 1);
        calendar.set(Calendar.DAY_OF_MONTH, 1);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar;
    }

    /**
     * 获取今年指定月份最后一个时刻的日历对象
     *
     * @param month 指定的月份，参数范围[1,12]
     * @param hour  所在时区相对于零时区偏移的小时数
     * @return 该时区今年指定月份最后一天的23:59:59 999时刻的日历对象
     */
    public static Calendar getNowYearLastTimeByMonth(Integer month, Integer hour) {
        Calendar calendar = ToolForDateTime.getNowYearFirstTimeByMonth(month, hour);
        int day = 0;
        switch (month) {
            case 1:
            case 3:
            case 5:
            case 8:
            case 10:
            case 7:
            case 12:
                day = 31;
                break;
            case 2:
                day = getNowYearFebruaryLastDay(8);
                break;
            case 4:
            case 6:
            case 9:
            case 11:
                day = 30;
                break;
        }
        calendar.set(Calendar.DAY_OF_MONTH, day);
        calendar.set(Calendar.HOUR_OF_DAY, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        calendar.set(Calendar.MILLISECOND, 999);
        return calendar;
    }

    /**
     * 获取指定年、指定月份的最大天数
     *
     * @param year  指定的年份
     * @param month 指定的月份
     */
    public static Integer getMaxDayBy(int year, int month) {
        Calendar calendar = new Calendar.Builder() {{
            setDate(year, month - 1, 1);
            setTimeOfDay(0, 0, 0, 0);
        }}.build();
        calendar.add(Calendar.MONTH, 1);
        calendar.add(Calendar.DAY_OF_YEAR, -1);
        return getDayByCalendar(calendar);
    }

    /**
     * 获取指定指定年、指定月份的第一个时刻的日历
     *
     * @param year  年份
     * @param month 月份
     */
    public static Calendar getFirstTimeCalendarForTheMonth(int year, int month) {
        return new Calendar.Builder() {{
            setDate(year, month - 1, 1);
            setTimeOfDay(0, 0, 0, 0);
        }}.build();
    }

    /**
     * 获取指定指定年、指定月份的最后一个个时刻的日历
     *
     * @param year  年份
     * @param month 月份
     */
    public static Calendar getLastTimeCalendarForTheMonth(int year, int month) {
        return new Calendar.Builder() {{
            setDate(year, month - 1, getMaxDayBy(year, month));
            setTimeOfDay(23, 59, 59, 999);
        }}.build();
    }

    /**
     * 将指定日历转成"yyyy-MM-dd HH:mm:ss SSS"格式的字符串
     */
    public static String getDateTimeLongStringBy(Calendar calendar) {
        return getStringByCalendarAndFormat(calendar, format_DateTimeLong);
    }

    /**
     * LocalDateTime 转换为string
     * yyyy-MM-dd HH:mm:ss SSS
     */
    public static String LocalDateTimeToString(LocalDateTime localDateTime){
        DateTimeFormatter df = DateTimeFormatter.ofPattern(format_DateTime);
        return df.format(localDateTime);
    }

    /**
     * LocalDateTime 转换为string
     * yyyy年MM月dd日 HH:mm:ss
     */
    public static String LocalDateTimeToStringText(LocalDateTime localDateTime){
        // 定义日期时间格式
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");
        return localDateTime.format(formatter);
    }





    /**
     * LocalDateTime 转换为string
     * yyyy-MM-dd
     */
    public static String LocalDateTimeToStringToDate(LocalDateTime localDateTime){
        DateTimeFormatter df = DateTimeFormatter.ofPattern(format_Date);
        return df.format(localDateTime);
    }

    /**
     * yyyy-MM-dd转化为 x年x月x日
     */
    public static String StringDateToYearMonthDay(String s){
        String[] split = s.split("-");
        return split[0] + "年" + split[1] + "月" + split[2] + "日";
    }

    /**
     * string 转换为LocalDateTime
     */
    public static LocalDateTime stringToLocalTime(String str){
        DateTimeFormatter df = DateTimeFormatter.ofPattern(format_DateTime);
        return LocalDateTime.parse(str, df);
    }

    /**
     * date转localDateTime
     */
    public static LocalDateTime dateToLocalDateTime(Date date){
        Instant instant = date.toInstant();
        ZoneId zoneId = ZoneId.systemDefault();

        //方式一
        ZonedDateTime zonedDateTime = instant.atZone(zoneId);

        //方式二 直接使用LocalDateTime.ofInstant
        // LocalDateTime localDateTime1 = LocalDateTime.ofInstant(instant, zoneId);

        return zonedDateTime.toLocalDateTime();
    }

    /**
     * LocalDate转Date
     * @param localDate 参数
     * @return Date
     */
    public static Date localDate2Date(LocalDate localDate) {
        if (null == localDate) {
            return null;
        }
        ZonedDateTime zonedDateTime = localDate.atStartOfDay(ZoneId.systemDefault());
        return Date.from(zonedDateTime.toInstant());
    }

    /**
     * Date转LocalDate
     * @param date LocalDate
     */
    public static LocalDate date2LocalDate(Date date) {
        if(null == date) {
            return null;
        }
        return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
    }

    // 03. java.util.Date --> java.time.LocalTime
    public static LocalTime UDateToLocalTime(Date date) {
        Instant instant = date.toInstant();
        ZoneId zone = ZoneId.systemDefault();
        LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
        return localDateTime.toLocalTime();
    }

    // string转date
    public static Date stringToDate(String str) throws ParseException {
        SimpleDateFormat ft = new SimpleDateFormat("yyyy-MM-dd");
        return ft.parse(str);
    }

    // date转string
    public static String dateToString(Date date){
        SimpleDateFormat ft = new SimpleDateFormat("yyyy-MM-dd");
        return ft.format(date);
    }

    /**
     * 将date转为localDate
     * @param date date
     * @return LocalDate
     */
    public static LocalDate convertDateToLocalDate(Date date) {
        // 将 Date 转换为 Instant
        Instant instant = date.toInstant();
        // 将 Instant 转换为 LocalDate
        return instant.atZone(ZoneId.systemDefault()).toLocalDate();
    }

    /**
     * 将localDate转为date
     * @param localDate localDate
     * @return Date
     */
    public static Date convertLocalDateToDate(LocalDate localDate) {
        return Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
    }



    private static final DateTimeFormatter TODAY_FORMATTER = DateTimeFormatter.ofPattern("HH:mm");
    private static final DateTimeFormatter YESTERDAY_FORMATTER = DateTimeFormatter.ofPattern("昨天 HH:mm");
    private static final DateTimeFormatter THE_DAY_BEFORE_YESTERDAY_FORMATTER = DateTimeFormatter.ofPattern("前天 HH:mm");
    private static final DateTimeFormatter THIS_YEAR_FORMATTER = DateTimeFormatter.ofPattern("MM-dd HH:mm");
    private static final DateTimeFormatter OTHER_YEARS_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");


    /**
     * 将参数传入一个类型为localdateTime的，
     * 要求返回一个字符串，分很多种形式：解析传入的LocalDateTime参数是否在今天，
     * 如果是今天就返回时分：12:30这种格式的，如果是昨天就返回 “昨天 12:30”这种格式的，
     * 如果是前天就返回“前天 12:30”这种格式的，如果再往前在今年内，就直接返回MM-DD HH:MM这种格式的，
     * 如果是去年前年还有更久之前就返回YYYY-MM-DD HH:MM这种格式的
     * @param dateTime dateTime
     * @return String
     */
    public static String formatDateTime(LocalDateTime dateTime) {
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime yesterday = now.minusDays(1);
        LocalDateTime theDayBeforeYesterday = now.minusDays(2);

        if (dateTime.toLocalDate().isEqual(now.toLocalDate())) {
            return dateTime.format(TODAY_FORMATTER);
        } else if (dateTime.toLocalDate().isEqual(yesterday.toLocalDate())) {
            return dateTime.format(YESTERDAY_FORMATTER);
        } else if (dateTime.toLocalDate().isEqual(theDayBeforeYesterday.toLocalDate())) {
            return dateTime.format(THE_DAY_BEFORE_YESTERDAY_FORMATTER);
        } else if (dateTime.getYear() == now.getYear()) {
            return dateTime.format(THIS_YEAR_FORMATTER);
        } else {
            return dateTime.format(OTHER_YEARS_FORMATTER);
        }
    }


    /**
     * 计算从createTime到现在nowDate有多少天了
     * @param createTime createTime
     * @param nowDate nowDate
     * @return Long
     */
    public static Long calculateDayNum(LocalDateTime createTime, LocalDate nowDate){
        LocalDate registrationDate  = createTime.toLocalDate();
        // 计算注册日期到现在的天数差
        return ChronoUnit.DAYS.between(registrationDate, nowDate);
    }

    /**
     * 根据生日计算到现在多少岁了
     * @param birthDate birthDate
     * @return int
     */
    public static int calculateAge(LocalDate birthDate){
        LocalDate currentDate = LocalDate.now();
        Period period = Period.between(birthDate, currentDate);
        return period.getYears();
    }

    public static void main(String[] args) {
        System.out.println(stringToLocalTime("2021-12-23 17:00:26"));
    }

}
